1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.codehaus.groovy.control;
20  
21  import org.codehaus.groovy.control.customizers.CompilationCustomizer;
22  import org.codehaus.groovy.control.io.NullWriter;
23  import org.codehaus.groovy.control.messages.WarningMessage;
24  
25  import java.io.File;
26  import java.io.PrintWriter;
27  import java.util.*;
28  
29  /**
30   * Compilation control flags and coordination stuff.
31   *
32   * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
33   * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
34   * @author <a href="mailto:jim@pagesmiths.com">Jim White</a>
35   * @author <a href="mailto:cedric.champeau@gmail.com">Cedric Champeau</a>
36   */
37  
38  public class CompilerConfiguration {
39  
40      private static final String JDK5_CLASSNAME_CHECK = "java.lang.annotation.Annotation";
41  
42      /** This (<code>"1.4"</code>) is the value for targetBytecode to compile for a JDK 1.4. **/
43      public static final String JDK4 = "1.4";
44      /** This (<code>"1.5"</code>) is the value for targetBytecode to compile for a JDK 1.5. **/
45      public static final String JDK5 = "1.5";
46      /** This (<code>"1.6"</code>) is the value for targetBytecode to compile for a JDK 1.6. **/
47      public static final String JDK6 = "1.6";
48      /** This (<code>"1.7"</code>) is the value for targetBytecode to compile for a JDK 1.7. **/
49      public static final String JDK7 = "1.7";
50      /** This (<code>"1.8"</code>) is the value for targetBytecode to compile for a JDK 1.8. **/
51      public static final String JDK8 = "1.8";
52  
53      /** This (<code>"1.5"</code>) is the value for targetBytecode to compile for a JDK 1.5 or later JVM. **/
54      public static final String POST_JDK5 = JDK5; // for backwards compatibility
55  
56      /** This (<code>"1.4"</code>) is the value for targetBytecode to compile for a JDK 1.4 JVM. **/
57      public static final String PRE_JDK5 = JDK4;
58  
59      private static final String[] ALLOWED_JDKS = { JDK4, JDK5, JDK6, JDK7, JDK8 };
60  
61      // Just call getVMVersion() once.
62      public static final String currentJVMVersion = getVMVersion();
63  
64      // Static initializers are executed in text order,
65      // therefore we must do this one last!
66      /**
67       *  A convenience for getting a default configuration.  Do not modify it!
68       *  See {@link #CompilerConfiguration(Properties)} for an example on how to
69       *  make a suitable copy to modify.  But if you're really starting from a
70       *  default context, then you probably just want <code>new CompilerConfiguration()</code>. 
71       */
72      public static final CompilerConfiguration DEFAULT = new CompilerConfiguration();
73      
74      /**
75       * See {@link WarningMessage} for levels.
76       */
77      private int warningLevel;
78  
79      /**
80       * Encoding for source files
81       */
82  
83      private String sourceEncoding;
84      /**
85       * A <code>PrintWriter</code> for communicating with the user
86       */
87  
88      private PrintWriter output;
89  
90      /**
91       * Directory into which to write classes
92       */
93      private File targetDirectory;
94  
95      /**
96       * Classpath for use during compilation
97       */
98      private LinkedList<String> classpath;
99  
100     /**
101      * If true, the compiler should produce action information
102      */
103     private boolean verbose;
104 
105     /**
106      * If true, debugging code should be activated
107      */
108     private boolean debug;
109 
110     /**
111      * The number of non-fatal errors to allow before bailing
112      */
113     private int tolerance;
114 
115     /**
116      * Base class name for scripts (must derive from Script)
117      */
118     private String scriptBaseClass;
119 
120     private ParserPluginFactory pluginFactory;
121 
122     /**
123      * extension used to find a groovy file
124      */
125     private String defaultScriptExtension;
126     
127     /**
128      * extensions used to find a groovy files
129      */
130     private Set<String> scriptExtensions = new LinkedHashSet<String>();
131     
132     /**
133      * if set to true recompilation is enabled
134      */
135     private boolean recompileGroovySource;
136     
137     /**
138      * sets the minimum of time after a script can be recompiled.
139      */
140     private int minimumRecompilationInterval;
141 
142     /**
143      * sets the bytecode version target
144      */
145     private String targetBytecode;
146 
147     /**
148      * options for joint compilation (null by default == no joint compilation)
149      */
150     private Map<String, Object> jointCompilationOptions;
151     
152     /**
153      * options for optimizations (empty map by default)
154      */
155     private Map<String, Boolean> optimizationOptions;
156 
157     private List<CompilationCustomizer> compilationCustomizers = new LinkedList<CompilationCustomizer>();
158 
159     /**
160      * Sets a list of global AST transformations which should not be loaded even if they are
161      * defined in META-INF/org.codehaus.groovy.transform.ASTTransformation files. By default,
162      * none is disabled.
163      */
164     private Set<String> disabledGlobalASTTransformations;
165 
166     private BytecodeProcessor bytecodePostprocessor;
167 
168     /**
169      * Sets the Flags to defaults.
170      */
171     public CompilerConfiguration() {
172         //
173         // Set in safe defaults
174 
175         setWarningLevel(WarningMessage.LIKELY_ERRORS);
176         setOutput(null);
177         setTargetDirectory((File) null);
178         setClasspath("");
179         setVerbose(false);
180         setDebug(false);
181         setTolerance(10);
182         setScriptBaseClass(null);
183         setRecompileGroovySource(false);
184         setMinimumRecompilationInterval(100);
185         // Target bytecode
186         String targetByteCode = null;
187         try {
188             targetByteCode = System.getProperty("groovy.target.bytecode", targetByteCode);
189         } catch (Exception e) {
190             // IGNORE
191         }
192         if(targetByteCode != null) {
193             setTargetBytecode(targetByteCode);
194         } else {
195             setTargetBytecode(getVMVersion());
196         }
197         String tmpDefaultScriptExtension = null;
198         try {
199             tmpDefaultScriptExtension = System.getProperty("groovy.default.scriptExtension");
200         } catch (Exception e) {
201             // IGNORE
202         }
203         if(tmpDefaultScriptExtension != null) {
204             setDefaultScriptExtension(tmpDefaultScriptExtension);
205         } else {
206             setDefaultScriptExtension(".groovy");
207         }
208 
209         //
210         // Source file encoding
211         String encoding = null;
212         try {
213             encoding = System.getProperty("file.encoding", "US-ASCII");
214         } catch (Exception e) {
215             // IGNORE
216         }
217         try {
218             encoding = System.getProperty("groovy.source.encoding", encoding);
219         } catch (Exception e) {
220             // IGNORE
221         }
222         setSourceEncoding(encoding);
223 
224         try {
225             setOutput(new PrintWriter(System.err));
226         } catch (Exception e) {
227             // IGNORE
228         }
229 
230         try {
231             String target = System.getProperty("groovy.target.directory");
232             if (target != null) {
233                 setTargetDirectory(target);
234             }
235         } catch (Exception e) {
236             // IGNORE
237         }
238 
239         boolean indy = false;
240         try {
241             indy = Boolean.getBoolean("groovy.target.indy");
242         } catch (Exception e) {
243             // IGNORE
244         }
245         if (DEFAULT!=null && Boolean.TRUE.equals(DEFAULT.getOptimizationOptions().get("indy"))) {
246             indy = true;
247         }
248         Map options = new HashMap<String,Boolean>(3);
249         if (indy) {
250             options.put("indy", Boolean.TRUE);
251         }
252         setOptimizationOptions(options);
253     }
254 
255     /**
256      * Copy constructor.  Use this if you have a mostly correct configuration
257      * for your compilation but you want to make a some changes programatically.
258      * An important reason to prefer this approach is that your code will most
259      * likely be forward compatible with future changes to this configuration API.
260      * <p>
261      * An example of this copy constructor at work:
262      * <pre>
263      *    // In all likelihood there is already a configuration in your code's context
264      *    // for you to copy, but for the sake of this example we'll use the global default.
265      *    CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
266      *    myConfiguration.setDebug(true);
267      *</pre>
268      *
269      * @param configuration The configuration to copy.
270      */
271     public CompilerConfiguration(CompilerConfiguration configuration) {
272         setWarningLevel(configuration.getWarningLevel());
273         setOutput(configuration.getOutput());
274         setTargetDirectory(configuration.getTargetDirectory());
275         setClasspathList(new LinkedList<String>(configuration.getClasspath()));
276         setVerbose(configuration.getVerbose());
277         setDebug(configuration.getDebug());
278         setTolerance(configuration.getTolerance());
279         setScriptBaseClass(configuration.getScriptBaseClass());
280         setRecompileGroovySource(configuration.getRecompileGroovySource());
281         setMinimumRecompilationInterval(configuration.getMinimumRecompilationInterval());
282         setTargetBytecode(configuration.getTargetBytecode());
283         setDefaultScriptExtension(configuration.getDefaultScriptExtension());
284         setSourceEncoding(configuration.getSourceEncoding());
285         setOutput(configuration.getOutput());
286         setTargetDirectory(configuration.getTargetDirectory());
287         Map<String, Object> jointCompilationOptions = configuration.getJointCompilationOptions();
288         if (jointCompilationOptions != null) {
289             jointCompilationOptions = new HashMap<String, Object>(jointCompilationOptions);
290         }
291         setJointCompilationOptions(jointCompilationOptions);
292         setPluginFactory(configuration.getPluginFactory());
293         setScriptExtensions(configuration.getScriptExtensions());
294         setOptimizationOptions(new HashMap<String, Boolean>(configuration.getOptimizationOptions()));
295     }
296 
297     /**
298      * Sets the Flags to the specified configuration, with defaults
299      * for those not supplied.
300      * Note that those "defaults" here do <em>not</em> include checking the
301      * settings in {@link System#getProperties()} in general, only file.encoding, 
302      * groovy.target.directory and groovy.source.encoding are.
303      * <p>
304      * If you want to set a few flags but keep Groovy's default
305      * configuration behavior then be sure to make your settings in
306      * a Properties that is backed by <code>System.getProperties()</code> (which
307      * is done using this constructor). That might be done like this:
308      * <pre>
309      * Properties myProperties = new Properties(System.getProperties());
310      * myProperties.setProperty("groovy.output.debug", "true");
311      * myConfiguration = new CompilerConfiguration(myProperties);
312      * </pre>
313      * And you also have to contend with a possible SecurityException when
314      * getting the system properties (See {@link java.lang.System#getProperties()}).
315      * A safer approach would be to copy a default
316      * CompilerConfiguration and make your changes there using the setter:
317      * <pre>
318      * // In all likelihood there is already a configuration for you to copy,
319      * // but for the sake of this example we'll use the global default.
320      * CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
321      * myConfiguration.setDebug(true);
322      * </pre>
323      * Another reason to use the copy constructor rather than this one is that you
324      * must call {@link #setOutput}.  Calling <code>setOutput(null)</code> is valid and will
325      * set up a <code>PrintWriter</code> to a bit bucket.  The copy constructor will of course set
326      * the same one as the original.
327      * <p>
328      * <table summary="Groovy Compiler Configuration Properties">
329      *   <tr>
330      *      <th>Property Key</th><th>Get/Set Property Name</th>
331      *   </tr>
332      *      <tr>
333      *      <td><code>"groovy.warnings"</code></td><td>{@link #getWarningLevel}</td></tr>
334      *      <tr><td><code>"groovy.source.encoding"</code></td><td>{@link #getSourceEncoding}</td></tr>
335      *      <tr><td><code>"groovy.target.directory"</code></td><td>{@link #getTargetDirectory}</td></tr>
336      *      <tr><td><code>"groovy.target.bytecode"</code></td><td>{@link #getTargetBytecode}</td></tr>
337      *      <tr><td><code>"groovy.classpath"</code></td><td>{@link #getClasspath}</td></tr>
338      *      <tr><td><code>"groovy.output.verbose"</code></td><td>{@link #getVerbose}</td></tr>
339      *      <tr><td><code>"groovy.output.debug"</code></td><td>{@link #getDebug}</td></tr>
340      *      <tr><td><code>"groovy.errors.tolerance"</code></td><td>{@link #getTolerance}</td></tr>
341      *      <tr><td><code>"groovy.script.extension"</code></td><td>{@link #getDefaultScriptExtension}</td></tr>
342      *      <tr><td><code>"groovy.script.base"</code></td><td>{@link #getScriptBaseClass}</td></tr>
343      *      <tr><td><code>"groovy.recompile"</code></td><td>{@link #getRecompileGroovySource}</td></tr>
344      *      <tr><td><code>"groovy.recompile.minimumInterval"</code></td><td>{@link #getMinimumRecompilationInterval}</td></tr>
345      *      <tr><td>
346      *   </tr>
347      * </table>
348      *
349      * @param configuration The properties to get flag values from.
350      */
351     public CompilerConfiguration(Properties configuration) throws ConfigurationException {
352         this();
353         configure(configuration);
354     }
355 
356     /**
357      * Checks if the specified bytecode version string represents a JDK 1.5+ compatible
358      * bytecode version.
359      * @param bytecodeVersion the bytecode version string (1.4, 1.5, 1.6, 1.7 or 1.8)
360      * @return true if the bytecode version is JDK 1.5+
361      */
362     public static boolean isPostJDK5(String bytecodeVersion) {
363         return JDK5.equals(bytecodeVersion)
364             || JDK6.equals(bytecodeVersion)
365             || JDK7.equals(bytecodeVersion)
366             || JDK8.equals(bytecodeVersion);
367     }
368 
369     /**
370      * Checks if the specified bytecode version string represents a JDK 1.7+ compatible
371      * bytecode version.
372      * @param bytecodeVersion the bytecode version string (1.4, 1.5, 1.6, 1.7 or 1.8)
373      * @return true if the bytecode version is JDK 1.7+
374      */
375     public static boolean isPostJDK7(String bytecodeVersion) {
376         return JDK7.equals(bytecodeVersion)
377             || JDK8.equals(bytecodeVersion);
378     }
379 
380     /**
381      * Method to configure a this CompilerConfiguration by using Properties.
382      * For a list of available properties look at {link {@link #CompilerConfiguration(Properties)}.
383      * @param configuration The properties to get flag values from.
384      */
385     public void configure(Properties configuration) throws ConfigurationException {
386         String text = null;
387         int numeric = 0;
388 
389         //
390         // Warning level
391 
392         numeric = getWarningLevel();
393         try {
394             text = configuration.getProperty("groovy.warnings", "likely errors");
395             numeric = Integer.parseInt(text);
396         } catch (NumberFormatException e) {
397             text = text.toLowerCase();
398             if (text.equals("none")) {
399                 numeric = WarningMessage.NONE;
400             }
401             else if (text.startsWith("likely")) {
402                 numeric = WarningMessage.LIKELY_ERRORS;
403             }
404             else if (text.startsWith("possible")) {
405                 numeric = WarningMessage.POSSIBLE_ERRORS;
406             }
407             else if (text.startsWith("paranoia")) {
408                 numeric = WarningMessage.PARANOIA;
409             }
410             else {
411                 throw new ConfigurationException("unrecognized groovy.warnings: " + text);
412             }
413         }
414         setWarningLevel(numeric);
415 
416         // 
417         // Source file encoding 
418         // 
419         text = configuration.getProperty("groovy.source.encoding");
420         if (text == null) {
421             text = configuration.getProperty("file.encoding", "US-ASCII");
422         }
423         setSourceEncoding(text);
424 
425         //
426         // Target directory for classes
427         //
428         text = configuration.getProperty("groovy.target.directory");
429         if (text != null) setTargetDirectory(text);
430         
431         text = configuration.getProperty("groovy.target.bytecode");
432         if (text != null) setTargetBytecode(text);
433         
434         //
435         // Classpath
436         //
437         text = configuration.getProperty("groovy.classpath");
438         if (text != null) setClasspath(text);
439 
440         //
441         // Verbosity
442         //
443         text = configuration.getProperty("groovy.output.verbose");
444         if (text != null && text.equalsIgnoreCase("true")) setVerbose(true);
445 
446         //
447         // Debugging
448         //
449         text = configuration.getProperty("groovy.output.debug");
450         if (text != null && text.equalsIgnoreCase("true")) setDebug(true);
451 
452         //
453         // Tolerance
454         // 
455         numeric = 10;
456         try {
457             text = configuration.getProperty("groovy.errors.tolerance", "10");
458             numeric = Integer.parseInt(text);
459         } catch (NumberFormatException e) {
460             throw new ConfigurationException(e);
461         }
462         setTolerance(numeric);
463 
464         //
465         // Script Base Class
466         //
467         text = configuration.getProperty("groovy.script.base");
468         if (text!=null) setScriptBaseClass(text);
469         
470         //
471         // recompilation options
472         //
473         text = configuration.getProperty("groovy.recompile");
474         if (text != null) {
475             setRecompileGroovySource(text.equalsIgnoreCase("true"));
476         }
477         
478         numeric = 100;
479         try {
480             text = configuration.getProperty("groovy.recompile.minimumIntervall");
481             if (text==null) text = configuration.getProperty("groovy.recompile.minimumInterval");
482             if (text!=null) {
483                 numeric = Integer.parseInt(text);
484             } else {
485                 numeric = 100;
486             }
487         } catch (NumberFormatException e) {
488             throw new ConfigurationException(e);
489         }
490         setMinimumRecompilationInterval(numeric);
491 
492         // disabled global AST transformations
493         text = configuration.getProperty("groovy.disabled.global.ast.transformations");
494         if (text!=null) {
495             String[] classNames = text.split(",\\s*}");
496             Set<String> blacklist = new HashSet<String>(Arrays.asList(classNames));
497             setDisabledGlobalASTTransformations(blacklist);
498         }
499     }
500 
501     /**
502      * Gets the currently configured warning level.  See WarningMessage
503      * for level details.
504      */
505     public int getWarningLevel() {
506         return this.warningLevel;
507     }
508 
509     /**
510      * Sets the warning level.  See WarningMessage for level details.
511      */
512     public void setWarningLevel(int level) {
513         if (level < WarningMessage.NONE || level > WarningMessage.PARANOIA) {
514             this.warningLevel = WarningMessage.LIKELY_ERRORS;
515         }
516         else {
517             this.warningLevel = level;
518         }
519     }
520 
521     /**
522      * Gets the currently configured source file encoding.
523      */
524     public String getSourceEncoding() {
525         return this.sourceEncoding;
526     }
527 
528     /**
529      * Sets the encoding to be used when reading source files.
530      */
531     public void setSourceEncoding(String encoding) {
532         if (encoding == null) encoding = "US-ASCII";
533         this.sourceEncoding = encoding;
534     }
535 
536     /**
537      * Gets the currently configured output writer.
538      */
539     public PrintWriter getOutput() {
540         return this.output;
541     }
542 
543     /**
544      * Sets the output writer.
545      */
546     public void setOutput(PrintWriter output) {
547         if (output == null) {
548             this.output = new PrintWriter(NullWriter.DEFAULT);
549         }
550         else {
551             this.output = output;
552         }
553     }
554 
555     /**
556      * Gets the target directory for writing classes.
557      */
558     public File getTargetDirectory() {
559         return this.targetDirectory;
560     }
561 
562     /**
563      * Sets the target directory.
564      */
565     public void setTargetDirectory(String directory) {
566         if (directory != null && directory.length() > 0) {
567             this.targetDirectory = new File(directory);
568         } else {
569             this.targetDirectory = null;
570         }
571     }
572 
573     /**
574      * Sets the target directory.
575      */
576     public void setTargetDirectory(File directory) {
577         this.targetDirectory = directory;
578     }
579 
580     /**
581      * @return the classpath
582      */
583     public List<String> getClasspath() {
584         return this.classpath;
585     }
586 
587     /**
588      * Sets the classpath.
589      */
590     public void setClasspath(String classpath) {
591         this.classpath = new LinkedList<String>();
592         StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
593         while (tokenizer.hasMoreTokens()) {
594             this.classpath.add(tokenizer.nextToken());
595         }
596     }
597     
598     /**
599      * sets the classpath using a list of Strings
600      * @param parts list of strings containing the classpath parts
601      */
602     public void setClasspathList(List<String> parts) {
603         this.classpath = new LinkedList<String>(parts);
604     }
605 
606     /**
607      * Returns true if verbose operation has been requested.
608      */
609     public boolean getVerbose() {
610         return this.verbose;
611     }
612 
613     /**
614      * Turns verbose operation on or off.
615      */
616     public void setVerbose(boolean verbose) {
617         this.verbose = verbose;
618     }
619 
620     /**
621      * Returns true if debugging operation has been requested.
622      */
623     public boolean getDebug() {
624         return this.debug;
625     }
626 
627     /**
628      * Turns debugging operation on or off.
629      */
630     public void setDebug(boolean debug) {
631         this.debug = debug;
632     }
633 
634     /**
635      * Returns the requested error tolerance.
636      */
637     public int getTolerance() {
638         return this.tolerance;
639     }
640 
641     /**
642      * Sets the error tolerance, which is the number of
643      * non-fatal errors (per unit) that should be tolerated before
644      * compilation is aborted.
645      */
646     public void setTolerance(int tolerance) {
647         this.tolerance = tolerance;
648     }
649 
650     /**
651      * Gets the name of the base class for scripts.  It must be a subclass
652      * of Script.
653      */
654     public String getScriptBaseClass() {
655         return this.scriptBaseClass;
656     }
657 
658     /**
659      * Sets the name of the base class for scripts.  It must be a subclass
660      * of Script.
661      */
662     public void setScriptBaseClass(String scriptBaseClass) {
663         this.scriptBaseClass = scriptBaseClass;
664     }
665 
666     public ParserPluginFactory getPluginFactory() {
667         if (pluginFactory == null) {
668             pluginFactory = ParserPluginFactory.newInstance(true);
669         }
670         return pluginFactory;
671     }
672 
673     public void setPluginFactory(ParserPluginFactory pluginFactory) {
674         this.pluginFactory = pluginFactory;
675     }
676 
677     public void setScriptExtensions(Set<String> scriptExtensions) {
678         if(scriptExtensions == null) scriptExtensions = new LinkedHashSet<String>();
679         this.scriptExtensions = scriptExtensions;
680     }
681     
682     public Set<String> getScriptExtensions() {
683         if(scriptExtensions == null || scriptExtensions.isEmpty()) {
684             /*
685              *  this happens 
686              *  *    when groovyc calls FileSystemCompiler in forked mode, or
687              *  *    when FileSystemCompiler is run from the command line directly, or
688              *  *    when groovy was not started using groovyc or FileSystemCompiler either
689              */
690             scriptExtensions = SourceExtensionHandler.getRegisteredExtensions(
691                     this.getClass().getClassLoader());
692         }
693         return scriptExtensions;
694     }
695     
696     public String getDefaultScriptExtension() {
697         return defaultScriptExtension;
698     }
699 
700 
701     public void setDefaultScriptExtension(String defaultScriptExtension) {
702         this.defaultScriptExtension = defaultScriptExtension;
703     }
704     
705     public void setRecompileGroovySource(boolean recompile) {
706         recompileGroovySource = recompile;
707     }
708     
709     public boolean getRecompileGroovySource(){
710         return recompileGroovySource;
711     }
712     
713     public void setMinimumRecompilationInterval(int time) {
714         minimumRecompilationInterval = Math.max(0,time);
715     }
716     
717     public int getMinimumRecompilationInterval() {
718         return minimumRecompilationInterval;
719     }
720 
721     /**
722      * Allow setting the bytecode compatibility. The parameter can take
723      * one of the values <tt>1.7</tt>, <tt>1.6</tt>, <tt>1.5</tt> or <tt>1.4</tt>.
724      * If wrong parameter then the value will default to VM determined version.
725      * 
726      * @param version the bytecode compatibility mode
727      */
728     public void setTargetBytecode(String version) {
729         for (String allowedJdk : ALLOWED_JDKS) {
730             if (allowedJdk.equals(version)) {
731                 this.targetBytecode = version;
732             }
733         }
734     }
735 
736     /**
737      * Retrieves the compiler bytecode compatibility mode.
738      * 
739      * @return bytecode compatibility mode. Can be either <tt>1.5</tt> or <tt>1.4</tt>.
740      */
741     public String getTargetBytecode() {
742         return this.targetBytecode;
743     }
744     
745     private static String getVMVersion() {
746         try {
747             Class.forName(JDK5_CLASSNAME_CHECK);
748             return POST_JDK5;
749         } catch(Exception ex) {
750             // IGNORE
751         }
752         return PRE_JDK5;
753     }
754     
755     /**
756      * Gets the joint compilation options for this configuration.
757      * @return the options
758      */
759     public Map<String, Object> getJointCompilationOptions() {
760         return jointCompilationOptions;
761     }
762     
763     /**
764      * Sets the joint compilation options for this configuration. 
765      * Using null will disable joint compilation.
766      * @param options the options
767      */
768     public void setJointCompilationOptions(Map<String, Object> options) {
769         jointCompilationOptions = options;
770     }
771 
772     /**
773      * Gets the optimization options for this configuration.
774      * @return the options (always not null)
775      */
776     public Map<String, Boolean> getOptimizationOptions() {
777         return optimizationOptions;
778     }
779     
780     /**
781      * Sets the optimization options for this configuration. 
782      * No entry or a true for that entry means to enable that optimization, 
783      * a false means the optimization is disabled. 
784      * Valid keys are "all" and "int".
785      * @param options the options.
786      * @throws IllegalArgumentException if the options are null
787      */
788     public void setOptimizationOptions(Map<String, Boolean> options) {
789         if (options==null) throw new IllegalArgumentException("provided option map must not be null");
790         optimizationOptions = options;
791     }
792 
793     /**
794      * Adds compilation customizers to the compilation process. A compilation customizer is a class node
795      * operation which performs various operations going from adding imports to access control.
796      * @param customizers the list of customizers to be added
797      * @return this configuration instance
798      */
799     public CompilerConfiguration addCompilationCustomizers(CompilationCustomizer... customizers) {
800         if (customizers==null) throw new IllegalArgumentException("provided customizers list must not be null");
801         compilationCustomizers.addAll(Arrays.asList(customizers));
802         return this;
803     }
804 
805     /**
806      * Returns the list of compilation customizers.
807      * @return the customizers (always not null)
808      */
809     public List<CompilationCustomizer> getCompilationCustomizers() {
810         return compilationCustomizers;
811     }
812 
813     /**
814      * Returns the list of disabled global AST transformation class names.
815      * @return a list of global AST transformation fully qualified class names
816      */
817     public Set<String> getDisabledGlobalASTTransformations() {
818         return disabledGlobalASTTransformations;
819     }
820 
821     /**
822      * Disables global AST transformations. In order to avoid class loading side effects, it is not recommended
823      * to use MyASTTransformation.class.getName() by directly use the class name as a string. Disabled AST transformations
824      * only apply to automatically loaded global AST transformations, that is to say transformations defined in a
825      * META-INF/org.codehaus.groovy.transform.ASTTransformation file. If you explicitly add a global AST transformation
826      * in your compilation process, for example using the {@link org.codehaus.groovy.control.customizers.ASTTransformationCustomizer} or
827      * using a {@link org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation}, then nothing will prevent
828      * the transformation from being loaded.
829      * @param disabledGlobalASTTransformations a set of fully qualified class names of global AST transformations
830      * which should not be loaded.
831      */
832     public void setDisabledGlobalASTTransformations(final Set<String> disabledGlobalASTTransformations) {
833         this.disabledGlobalASTTransformations = disabledGlobalASTTransformations;
834     }
835 
836     public BytecodeProcessor getBytecodePostprocessor() {
837         return bytecodePostprocessor;
838     }
839 
840     public void setBytecodePostprocessor(final BytecodeProcessor bytecodePostprocessor) {
841         this.bytecodePostprocessor = bytecodePostprocessor;
842     }
843 }